package com.hmdp.utils;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Date;

/**
 * 作者：無言
 * 时间：2022/5/11 19:58
 * 描述：永无BUG
 * 全局唯一ID的实现： 比如mysql中的订单生成的的id是自增的，在超级多的数据下，
 * 需要分很多表，但是每张表的id都会从1开始，那么就会存在重复的id这种问题，
 * 另外一个问题就是，用户可以根据id猜测商品的前面的所有id的数量（你买了的时候id=100， 可以猜到前面有99个）
 * 解决上面的问题，可以通过全局唯一id,redis的id可以实现唯一，而且高并发，安全
 * 唯一id形式： 符号位(1bit)-时间戳(31bit)-序列号(32bit)
 *        0(1bit的符号位)-000...000(31bit为时间戳)-000..0000(32bit的序列号)
 */

@Component
public class RedisIdWorker {

    private static final long BEGIN_TIMESTAMP = 1640995200L; //设置的时间戳的起始时间2022.1.1 0:0:0
    private static final int COUNT_BITS = 32; //序列号位数

    private StringRedisTemplate stringRedisTemplate;

    public RedisIdWorker(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }


    public Long nextId(String keyPrefix){
        //1.生成时间戳
        LocalDateTime now =  LocalDateTime.now();
        long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
        long timestamp = nowSecond - BEGIN_TIMESTAMP; //时间戳

        //2.生成序列号
        //2.1 获取当前日期，精确到天
        String  date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        //2.2 自增
        long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);
        //3.拼接返回
        return timestamp << COUNT_BITS | count ;
        /**
         * 因为时间错是long类型，序列号是String 类型，所以不能直接拼接，我们将时间戳左移序列号需要的位置（比如32位），
         * 那么此时时间戳的右边的32位都是0， 那么此时再和时间戳进行或运算，接拼成功了
         * 比如 时间戳 timestamp = 1111   ，序列号 count = 10101
         * 序列号是5位， 那 timestamp << 5 = 1111 00000 (右边空出5个0)
         * 然后  timestamp | count = 1111 10101
         * 经过上面两步，就实现了拼接了
         */
    }

    /*
    public static void main(String[] args){
        LocalDateTime time = LocalDateTime.of(2022, 1, 1, 0, 0, 0);
        long second = time.toEpochSecond(ZoneOffset.UTC); //2022年1月1日 0:0:0转化为秒
        System.out.println(second); //1640995200
    }
    */

}
